// +-------------------------------------------------
// | minihttpd.c 
// | 小型HTTP服务器 
// +-------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "csock.h"
#include "threadpool.h"

#define MHD_SIZE        2048        //缓存大小
#define MHD_PORT        8080        //端口
#define MHD_LISTEN_LEN  100         //listen参数
#define MHD_TIMEOUT     10          //超时时间
#define MHD_WWW_ROOT    "./www"     //网站根目录
#define MHD_TMP_PATH    "./tmp"     //缓存目录

#ifndef FREE
#define FREE(x) if (x) { free(x); x = NULL; }
#endif

typedef struct MiniThreadData {
    int sock;
    int click;
} MiniThreadData;

static long minirand = 0;           //连接计数器

static void minihttpd_main (int sock);
static void minihttpd_accept_cb2 (void *param) {
    MiniThreadData *data = (MiniThreadData *)param;
    srand(time(NULL) + data->click);
    minihttpd_main(data->sock);
    free(data);
}
static void minihttpd_accept_cb (void *param, int sock) {
    ThreadPool *pool = (ThreadPool *)param;
    MiniThreadData *data = (MiniThreadData *)calloc(1, sizeof(MiniThreadData));

    if (!data) {
        cSOCK_CloseNow(sock);
        return;
    }

    data->sock = sock;
    data->click = minirand++;
    cSOCK_SetTimeout(data->sock, MHD_TIMEOUT);

    if (!thpool_join(pool, minihttpd_accept_cb2, data))
        cSOCK_CloseNow(sock);

    /** 重置连接计数器 **/
    if (minirand >= 1000000) minirand = 0;
}

/** 检查配置 **/
static void minihttpd_check () {
    FILE *fp = NULL;
    char tmp[MHD_SIZE] = {0};

    /** 测试tmp目录是否能用 **/
    snprintf(tmp, MHD_SIZE - 1, "%s/minihttpd_check.txt", MHD_TMP_PATH);
    if (fp = fopen(tmp, "wb+")) {
        fclose(fp);
        remove(tmp);
    } else {
        fprintf(stderr, "tmp_path error \n");
        exit(0);
    }
}

/** 开启服务器 **/
static void minihttpd_run () {
    ThreadPool *pool = NULL;
    int ret = 0;

    pool = thpool_create(MHD_LISTEN_LEN);
    if (!pool) {
        fprintf(stderr, "OOM\n");
        return;
    }

    ret = csockserver(MHD_PORT, pool, minihttpd_accept_cb);
    fprintf(stderr, "ret = [%d]\n", ret);
}

/** 启动 **/
int main (int argc, char *argv[]) {
    minihttpd_check();
    minihttpd_run();
    return 1;
}

// +-------------------------------------------------
// | 内部函数 
// +-------------------------------------------------

/** 随机字符串 **/
static char *_randstr (size_t n) {
    char *s = NULL;
    size_t i = 0, flag = 0;

    s = (char *)calloc(n + 1, sizeof(char));
    if (!s) return NULL;

    for (i = 0; i < n; i++) {
        flag = rand() % 4;
        if (flag == 0) s[i] = 'A' + rand() % 26;
        else if (flag == 1) s[i] = 'a' + rand() % 26;
        else if (flag == 2) s[i] = '0' + rand() % 10;
        else s[i] = '_';
    }
    return s;
}

/** 分割字符串 **/
static char **_splite (const char *s, const char *c, size_t *n) {
    char **retData = NULL;

    const char *p = s, *left = s;
    size_t i = 0, retLen = 0;

    if (!s || !c || !n) return NULL;

    if (*s == '\0' || *c == '\0') {
        retData = (char **)calloc(1, sizeof(char *));
        if (!retData) return NULL;
        retData[0] = strdup(s);
        if (!retData[0]) { free(retData); return NULL; }
        *n = 1;
        return retData;
    }

    /** 计算个数 **/
    retLen = 1;
    while (p = strstr(p, c)) {
        p += strlen(c); retLen++;
    }
    
    retData = (char **)calloc(retLen, sizeof(char *));
    if (!retData) return NULL;

    p = s;
    while ((p = strstr(p, c)) != NULL && i < retLen) {
        if (p == left) {
            retData[i] = strdup("\0");
        } else {
            retData[i] = (char *)calloc(p - left + 1, sizeof(char));
            if (retData[i]) memcpy(retData[i], left, p - left);
        }

        i++;
        p += strlen(c);
        left = p;
    }
    retData[i++] = strdup(left);

    /** 检查空值 **/
    for (i = 0; i < retLen; i++) {
        if (retData[i] == NULL) {
            for (i = 0; i < retLen; i++)
                if (retData[i]) free(retData[i]);
            free(retData);
            return NULL;
        }
    }

    *n = retLen;
    return retData;
}

/** 分割字符串(必须且只分割一次) **/
static char **_splite2 (const char *s, const char *c) {
    char **retData = NULL;
    char *p = NULL, *buf = NULL;

    if (!s || !c || *s == '\0' || *c == '\0' || (buf = strdup(s)) == NULL)
        return NULL;

    p = strstr(buf, c);
    if (p == buf) { free(buf); return NULL; }
    *p = '\0';

    retData = (char **)calloc(2, sizeof(char *));
    if (!retData) { free(buf); return NULL; }

    retData[0] = strdup(buf);
    retData[1] = strdup(p + strlen(c));
    if (!retData[0] || !retData[1]) {
        if (retData[0]) free(retData[0]);
        if (retData[1]) free(retData[1]);
        free(retData);
        free(buf);
        return NULL;
    }

    free(buf);
    return retData;
}

static int _hex2num (char c) {
    if (c >= '0' && c <= '9') return c - '0';
    if (c >= 'a' && c <= 'z') return c - 'a' + 10;
    if (c >= 'A' && c <= 'Z') return c - 'A' + 10;
    return '\0';
}

static int _urldecode (const char *s1, char *s2, size_t size) {
    int i = 0, s2i = 0;
    int s1Len = strlen(s1);

    if (!s1 || !s2 || size == 0) return 0;

    for (i = 0; i < s1Len && s2i < size; i++) {
        if (s1[i] != '%') s2[s2i++] = s1[i];
        else s2[s2i++] = _hex2num(s1[++i]) * 16 + _hex2num(s1[++i]);
    }

    s2[s2i] = '\0';
    return s2i;
}

static char *_urldecode2 (const char *s) {
    char *data = NULL;

    if (!s) return NULL;
    if (*s == '\0') return strdup("\0");

    data = (char *)calloc(strlen(s) + 1, sizeof(char));
    if (!data) return 0;
    _urldecode(s, data, strlen(s));
    return data;
}

// +-------------------------------------------------
// | 耦合的内部函数 
// +-------------------------------------------------

typedef enum {
    MINI_HTTPD_GET  = 1,
    MINI_HTTPD_POST = 2
} MiniHTTPDMethod;

typedef enum {
    MINI_HTTPD_NULL   = 0,  //无数据
    MINI_HTTPD_MULTI  = 1,  //multipart/form-data; boundary=----
    MINI_HTTPD_X_WWW  = 2,  //application/x-www-form-urlencoded
    MINI_HTTPD_RAW    = 3   //一坨数据
} MiniHTTPDType;

typedef struct MiniHTTPDRequest {
    MiniHTTPDMethod method;

    //访问路径
    char path[MHD_SIZE];

    //GET数据
    char  **get_keys;
    char  **get_datas;
    size_t  get_len;

    //HEAD数据
    char  **head_keys;
    char  **head_datas;
    size_t  head_len;
    size_t  head_mem_len;

    //POST数据缓存文件
    char    post_tmp[MHD_SIZE];

    //数据类型
    MiniHTTPDType posttype;

    //POST数据
    char  **post_keys;
    char  **post_datas;
    size_t  post_len;
    size_t  post_mem_len;

    //文件
    char  **file_keys;
    char  **file_name;
    char  **file_path;
    size_t  file_len;
    size_t  file_mem_len;
} MiniHTTPDRequest;

/** 解析数据[a=b&c=d] **/
static void _minihttpd_decode_kv (const char *s, MiniHTTPDRequest *request) {
    char **datas = NULL, **datas2 = NULL;
    size_t datas_n = 0, datas_n2 = 0;
    char *keyd = NULL, *datad = NULL;

    size_t i = 0, j = 0, k = 0, l = 0;

    if (*s == '\0') return;
    datas = _splite(s, "&", &datas_n);
    if (!datas) return;

    request->get_keys  = (char **)calloc(datas_n, sizeof(char *));
    if (!request->get_keys) {
        for (i = 0; i < datas_n; i++) free(datas[i]);
        free(datas);
        return;
    }

    request->get_datas = (char **)calloc(datas_n, sizeof(char *));
    if (!request->get_datas) {
        for (i = 0; i < datas_n; i++) free(datas[i]);
        free(request->get_keys);
        request->get_keys = NULL;
        free(datas);
        return;
    }
    request->get_len = datas_n;

    for (i = 0; i < datas_n; i++) {
        datas2 = _splite2(datas[i], "=");
        free(datas[i]);
        if (!datas2) continue;

        /** URL解码 **/
        keyd = _urldecode2(datas2[0]);
        datad = _urldecode2(datas2[1]);
        if (!keyd || !datad) {
            free(datas2[0]);
            free(datas2[1]);
            free(datas2);
            if (keyd) free(keyd);
            if (datad) free(datad);
            continue;
        }
        free(datas2[0]);
        free(datas2[1]);

        /** 检查重复 **/
        k = 0;
        for (l = 0; l < j; l++) {
            if (strcmp(request->get_keys[l], keyd) == 0) {
                k = l; break;
            }
        }

        /** 保存键值 **/
        if (k == 0) {
            request->get_keys[j] = keyd;
            request->get_datas[j++] = datad;
        } else {
            free(request->get_keys[k]);
            free(request->get_datas[k]);
            request->get_keys[k] = keyd;
            request->get_datas[k] = datad;
        }

        free(datas2);
    }

    free(datas);
}

/** 解析第一行 [GET / HTTP/1.1] **/
static int _minihttpd_decode_gp (const char *s, MiniHTTPDRequest *request) {
    char *p = NULL;
    int size = 0;

    if (strncmp(s, "GET", 3) == 0) {
        s += 4;
        request->method = MINI_HTTPD_GET;
    } else if (strncmp(s, "POST", 4) == 0) {
        s += 5;
        request->method = MINI_HTTPD_POST;
    } else return 0;

    p = strstr(s, " HTTP/");
    if (!p) return 0;

    size = p - s;
    size = size > sizeof(request->path) - 1 ? sizeof(request->path) - 1 : size;
    if (size <= 0) return 0;

    strncpy(request->path, s, size);
    if (p = strstr(request->path, "?")) {
        *p = '\0';
        _minihttpd_decode_kv(p + 1, request);
    }

    /** 路径中不允许[..]存在 **/
    if (strstr(request->path, ".."))
        return 0;

    return 1;
}

/** 追加一个HEAD数据 **/
static void _minihttp_request_add_head (MiniHTTPDRequest *request, char *key, char *value) {
    char **head_keys = NULL, **head_datas = NULL;

    size_t i = 0;

    /** 长度不够追加5个 **/
    if (request->head_len == request->head_mem_len) {
        head_keys = (char **)calloc(request->head_mem_len + 5, sizeof(char *));
        if (!head_keys) return;

        head_datas = (char **)calloc(request->head_mem_len + 5, sizeof(char *));
        if (!head_datas) {
            free(head_keys); return;
        }

        for (i = 0; i < request->head_len; i++) {
            head_keys[i] = request->head_keys[i];
            head_datas[i] = request->head_datas[i];
        }

        if (request->head_keys) {
            free(request->head_keys);
            free(request->head_datas);
        }

        request->head_keys = head_keys;
        request->head_datas = head_datas;
        request->head_mem_len += 5;
    }

    value[strlen(value) - 1] = '\0';
    value[strlen(value) - 1] = '\0';

    request->head_keys[request->head_len]  = key;
    request->head_datas[request->head_len] = value;
    request->head_len++;
}

/** 解析HTTP头部[Cookies: xxx][Content-Type: yyy] **/
static void _minihttp_request_decode_head (const char *s, MiniHTTPDRequest *request) {
    char **data = NULL;

    data = _splite2(s, ": ");
    if (data) {
        _minihttp_request_add_head(request, data[0], data[1]);
        free(data);
    }
}

/** 解析name/filename **/
static void _minihttpd_decode_post2_1 (const char *tmp, char **name, char **filename) {
    char *left = NULL, *right = NULL;
    char *tmpName = NULL, *tmpFileName = NULL;

    left = strstr(tmp, "Content-Disposition: form-data; name=\"");
    if (!left) return; left += strlen("Content-Disposition: form-data; name=\"");

    right = strstr(left, "\"");
    if (!right || left == right) return;

    tmpName = (char *)calloc(right - left + 1, sizeof(char));
    if (!tmpName) return;
    strncpy(tmpName, left, right - left);
    *name = tmpName;

    left = strstr(right, "; filename=\"");
    if (!left) return; left += strlen("; filename=\"");

    right = strstr(left, "\"");
    if (!right || left == right) {      //filename=""
        *filename = strdup("");
        if (*filename == NULL) {
            free(tmpName);
            *name = NULL;
        }
        return;
    }

    tmpFileName = (char *)calloc(right - left + 1, sizeof(char));
    if (!tmpFileName) {
        *name = NULL;
        free(tmpName);
        return;
    }
    strncpy(tmpFileName, left, right - left);
    *filename = tmpFileName;
}

/** 寻找末尾 **/
static long _minihttpd_decode_post2_2 (FILE *handle, const char *label3) {
    char  *check     = NULL;
    size_t check_len = 0;
    int    ret       = 0;
    long   last      = 0;

    check_len = strlen(label3) + 1;
    check = (char *)calloc(check_len, sizeof(char));
    if (!check) return 0;

    while (1) {      
        last = ftell(handle);
        ret = fread(check, 1, check_len - 1, handle);
        if (ret != check_len - 1) {
            free(check); return 0;
        }
        if (strcmp(check, label3) == 0) {
            fseek(handle, last + 2, SEEK_SET);
            break;
        }
        fseek(handle, last + 1, SEEK_SET);
    }

    free(check);
    return last;
}

/** 添加POST数据 **/
static int _minihttpd_decode_post2_add1 (MiniHTTPDRequest *request, FILE *handle, const char *name, long start, long end) {
    char *key = NULL, *value = NULL;
    long  value_len = end - start + 1;
    long savePoint = ftell(handle);

    char **post_keys = NULL, **post_datas = NULL;
    size_t i = 0;

    key = strdup(name);
    if (!key) return 0;

    value = (char *)calloc(value_len, sizeof(char));
    if (!value) { free(key); return 0; }
    fseek(handle, start, SEEK_SET);
    fread(value, 1, end - start, handle);
    fseek(handle, savePoint, SEEK_SET);

    /** 长度不够追加5个 **/
    if (request->post_len == request->post_mem_len) {
        post_keys = (char **)calloc(request->post_mem_len + 5, sizeof(char *));
        if (!post_keys) { free(key); free(value); return 0; }

        post_datas = (char **)calloc(request->post_mem_len + 5, sizeof(char *));
        if (!post_datas) {
            free(post_keys); free(key); free(value);
            return 1;
        }

        for (i = 0; i < request->post_len; i++) {
            post_keys[i] = request->post_keys[i];
            post_datas[i] = request->post_datas[i];
        }

        if (request->post_keys) {
            free(request->post_keys);
            free(request->post_datas);
        }

        request->post_keys = post_keys;
        request->post_datas = post_datas;
        request->post_mem_len += 5;
    }

    request->post_keys[request->post_len]  = key;
    request->post_datas[request->post_len] = value;
    request->post_len++;
    return 1;
}

/** 添加POST文件 **/
static int _minihttpd_decode_post2_add2 (MiniHTTPDRequest *request, FILE *handle, const char *name, const char *filename, long start, long end) {
    const int randSize = 25;    //随机字符串大小

    char *file_key = NULL, *file_name = NULL, *file_path = NULL, *randomstr = NULL;
    FILE *file_handle = NULL;

    long savePoint = ftell(handle);
    char **file_keys_ = NULL, **file_name_ = NULL, **file_path_ = NULL;
    size_t i = 0;

    file_key = strdup(name);
    if (!file_key) return 0;

    file_name = strdup(filename);
    if (!file_name) {
        free(file_key);
        return 0;
    }

    randomstr = _randstr(randSize);
    if (!randomstr) {
        free(file_key);
        free(file_name);
        return 0;
    }

    file_path = (char *)calloc(strlen(MHD_TMP_PATH) + 50, sizeof(char));
    if (!file_path) {
        free(file_key);
        free(file_name);
        free(randomstr);
        return 0;
    }
    snprintf(file_path, strlen(MHD_TMP_PATH) + 50 - 1, "%s/%s", MHD_TMP_PATH, randomstr);

    file_handle = fopen(file_path, "wb+");
    if (!file_handle) {
        free(file_key);
        free(file_name);
        free(randomstr);
        free(file_path);
        return 0;
    }

    fseek(handle, start, SEEK_SET);
    memset(randomstr, 0, randSize);
    while (start < end) {
        long bufSize = (end - start) > randSize ? randSize : (end - start);
        fread(randomstr, 1, bufSize, handle);
        fwrite(randomstr, 1, bufSize, file_handle);
        start += bufSize;
    }
    fclose(file_handle);
    fseek(handle, savePoint, SEEK_SET);
    free(randomstr);

    /** 长度不够追加5个 **/
    if (request->file_len == request->file_mem_len) {
        file_keys_ = (char **)calloc(request->file_mem_len + 5, sizeof(char *));
        if (!file_keys_) {
            remove(file_path);
            free(file_key);
            free(file_name);
            free(file_path);
            return 0;
        }

        file_name_ = (char **)calloc(request->file_mem_len + 5, sizeof(char *));
        if (!file_name_) {
            free(file_keys_);
            remove(file_path);
            free(file_key);
            free(file_name);
            free(file_path);
            return 0;
        }

        file_path_ = (char **)calloc(request->file_mem_len + 5, sizeof(char *));
        if (!file_path_) {
            free(file_keys_);
            free(file_name_);
            remove(file_path);
            free(file_key);
            free(file_name);
            free(file_path);
            return 0;
        }

        for (i = 0; i < request->file_len; i++) {
            file_keys_[i] = request->file_keys[i];
            file_name_[i] = request->file_name[i];
            file_path_[i] = request->file_path[i];
        }

        if (request->file_keys) {
            free(request->file_keys);
            free(request->file_name);
            free(request->file_path);
        }

        request->file_keys = file_keys_;
        request->file_name = file_name_;
        request->file_path = file_path_;
        request->file_mem_len += 5;
    }

    request->file_keys[request->file_len] = file_key;
    request->file_name[request->file_len] = file_name;
    request->file_path[request->file_len] = file_path;
    request->file_len++;
}

/** 解析POST数据2 **/
static void _minihttpd_decode_post2 (MiniHTTPDRequest *request, const char *boundary, FILE *handle) {
    int  ret = 0;
    char tmp[MHD_SIZE] = {0};
    char label1[125] = {0}, label2[125] = {0}, label3[125] = {0};

    snprintf(label1, sizeof(label1) - 1, "--%s\r\n", boundary);
    snprintf(label2, sizeof(label2) - 1, "--%s--\r\n", boundary);
    snprintf(label3, sizeof(label3) - 1, "\r\n--%s", boundary);

    fseek(handle, 0, SEEK_SET);
    while (fgets(tmp, sizeof(tmp) - 1, handle)) {
        int flag = 0;
        long start = 0, end = 0;
        char *name = NULL, *data = NULL;
        char *filename = NULL, *tmppath = NULL;

        char *rets = NULL;

        // 检查头部
        if (strcmp(tmp, label1) != 0) break;

        while (1) {
            // 获得其他头部
            memset(tmp, 0, sizeof(tmp));
            rets = fgets(tmp, sizeof(tmp) - 1, handle);
            if (!rets) { flag = 1; break; }
            
            // 解析name,filename
            if (strstr(tmp, "Content-Disposition")) {
                _minihttpd_decode_post2_1(tmp, &name, &filename);
                if (!name) { flag = 1; break; }
            }

            // 结尾标识
            else if (strcmp(tmp, "\r\n") == 0) {
                flag = 2; break;
            }
        }

        // 异常
        if (flag == 1) {
            FREE(name); FREE(filename);
            break;
        }

        // 开始读取内容
        start = ftell(handle);

        // 检查到末尾
        end = _minihttpd_decode_post2_2(handle, label3);
        if (end < start) {
            FREE(name); FREE(filename);
            break;
        }

        if (!filename) {
            _minihttpd_decode_post2_add1(request, handle, name, start, end);
        }

        else if (filename && strlen(filename) != 0) {
            _minihttpd_decode_post2_add2(request, handle, name, filename, start, end);
        }

        // 走到这一步一般是没有上传文件
        else {
            FREE(name);
            FREE(filename);
        }

        memset(tmp, 0, sizeof(tmp));
    }
}

/** 解析POST数据3 **/
static void _minihttpd_decode_post3 (MiniHTTPDRequest *request, FILE *handle) {
    char **datas = NULL, **datas2 = NULL;
    size_t datas_n = 0, datas_n2 = 0;
    char *keyd = NULL, *datad = NULL;

    size_t i = 0, j = 0, k = 0, l = 0;

    char *s = NULL;
    int size = ftell(handle);
    fseek(handle, 0, SEEK_SET);

    s = (char *)calloc(size + 1, sizeof(char));
    if (!s) return;
    fread(s, 1, size, handle);

    datas = _splite(s, "&", &datas_n);
    if (!datas) { free(s); return; }

    request->post_keys  = (char **)calloc(datas_n, sizeof(char *));
    if (!request->post_keys) {
        for (i = 0; i < datas_n; i++) free(datas[i]);
        free(datas);
        free(s);
        return;
    }

    request->post_datas = (char **)calloc(datas_n, sizeof(char *));
    if (!request->post_datas) {
        for (i = 0; i < datas_n; i++) free(datas[i]);
        free(request->post_keys);
        request->post_keys = NULL;
        free(datas);
        free(s);
        return;
    }
    request->post_len = datas_n;

    for (i = 0; i < datas_n; i++) {
        datas2 = _splite2(datas[i], "=");
        free(datas[i]);
        if (!datas2) continue;

        /** URL解码 **/
        keyd = _urldecode2(datas2[0]);
        datad = _urldecode2(datas2[1]);
        if (!keyd || !datad) {
            free(datas2[0]);
            free(datas2[1]);
            free(datas2);
            if (keyd) free(keyd);
            if (datad) free(datad);
            continue;
        }
        free(datas2[0]);
        free(datas2[1]);

        /** 检查重复 **/
        k = 0;
        for (l = 0; l < j; l++) {
            if (strcmp(request->post_keys[l], keyd) == 0) {
                k = l; break;
            }
        }

        /** 保存键值 **/
        if (k == 0) {
            request->post_keys[j] = keyd;
            request->post_datas[j++] = datad;
        } else {
            free(request->post_keys[k]);
            free(request->post_datas[k]);
            request->post_keys[k] = keyd;
            request->post_datas[k] = datad;
        }

        free(datas2);
    }

    free(datas);
    free(s);
}

/** 解析POST数据 **/
static int _minihttpd_decode_post (int sock, MiniHTTPDRequest *request) {
    FILE *handle = NULL;
    char *randStr = NULL;
    char buf[MHD_SIZE] = {0};
    const char *Content_Type = NULL;
    int Content_Length = 0;

    size_t i = 0;
    int ret = 0, ret2 = 0;

    for (i = 0; i < request->head_len; i++) {
        if (strcmp(request->head_keys[i], "Content-Length") == 0) {
            Content_Length = atoi(request->head_datas[i]);
        }
        else if (strcmp(request->head_keys[i], "Content-Type") == 0) {
            Content_Type = request->head_datas[i];
        }
    }

    /** 没有数据 **/
    if (Content_Length == 0) return 1;

    randStr = _randstr(25);
    if (!randStr) return 0;
    snprintf(request->post_tmp, sizeof(request->post_tmp) - 1, "%s/%s", MHD_TMP_PATH, randStr);
    free(randStr);

    handle = fopen(request->post_tmp, "wb+");
    if (!handle) { 
        memset(request->post_tmp, 0, sizeof(request->post_tmp));
        return 0;
    }

    while (Content_Length > 0) {
        int recvSize = Content_Length > MHD_SIZE ? MHD_SIZE : Content_Length;
        ret = cSOCK_Recv(sock, buf, recvSize);
        ret2 = fwrite(buf, 1, ret, handle);

        if (ret != recvSize || ret2 != ret) {
            memset(request->post_tmp, 0, sizeof(request->post_tmp));
            fclose(handle);
            remove(request->post_tmp);
            return 0;
        }

        Content_Length -= recvSize;
    }

    request->posttype = MINI_HTTPD_NULL;

    /** 分析数据类型 **/
    if (!Content_Type) {
        request->posttype = MINI_HTTPD_RAW;
    }

    else if (strcmp(Content_Type, "application/x-www-form-urlencoded") == 0) {
        request->posttype = MINI_HTTPD_X_WWW;
    }

    else if (strncmp(Content_Type, "multipart/form-data; boundary=", 30) == 0) {
        request->posttype = MINI_HTTPD_MULTI;
    }

    else {
        request->posttype = MINI_HTTPD_RAW;
    }

    /** 分解键值对数据 **/
    if (request->posttype == MINI_HTTPD_X_WWW) {
        _minihttpd_decode_post3(request, handle);
    }

    /** 分解数据/文件 **/
    else if (request->posttype == MINI_HTTPD_MULTI) {
        if (strlen(Content_Type + 30) != 0) {
            _minihttpd_decode_post2(request, Content_Type + 30, handle);
        }
    }

    fclose(handle);
    return 1;
}

/** 获得GET内容 **/
static const char *_minihttpd_request_get_get (MiniHTTPDRequest *request, const char *key) {
    size_t i = 0;

    if (!request->get_datas) return NULL;

    for (i = 0; i < request->get_len; i++) {
        if (strcmp(request->get_keys[i], key) == 0)
            return request->get_datas[i];
    }
    return NULL;
}

/** 获得POST数据 **/
static char **_minihttpd_request_get_post (MiniHTTPDRequest *request, const char *key, size_t *n) {
    size_t i = 0, j = 0;
    char **ret_datas = NULL;
    size_t ret_len   = 0;
    
    if (!request->post_keys) return NULL;
    
    for (i = 0; i < request->post_len; i++) {
        if (strcmp(request->post_keys[i], key) == 0)
            ret_len++;
    }

    if (ret_len == 0) return NULL;

    ret_datas = (char **)calloc(ret_len, sizeof(char *));
    if (!ret_datas) return NULL;

    for (i = 0; i < request->post_len; i++) {
        if (strcmp(request->post_keys[i], key) == 0) {
            ret_datas[j++] = request->post_datas[i];
        }
    }

    *n = ret_len;
    return ret_datas;
}

/** 获得上传文件数据 **/
static size_t _minihttpd_request_get_file (MiniHTTPDRequest *request, const char *key, char ***file_name_, char ***file_path_) {
    char **file_name = NULL, **file_path = NULL;
    size_t ret_len   = 0, i = 0, j = 0;

    if (!request->file_keys) return 0;

    for (i = 0; i < request->file_len; i++) {
        if (strcmp(request->file_keys[i], key) == 0)
            ret_len++;
    }

    if (ret_len == 0) return 0;

    file_name = (char **)calloc(ret_len, sizeof(char *));
    if (!file_name) return 0;
    file_path = (char **)calloc(ret_len, sizeof(char *));
    if (!file_path) { free(file_name); return 0; }

    for (i = 0; i < request->file_len; i++) {
        if (strcmp(request->file_keys[i], key) == 0) {
            file_name[j] = request->file_name[i];
            file_path[j] = request->file_path[i];
            j++;
        }
    }

    *file_name_ = file_name;
    *file_path_ = file_path;
    return ret_len;
}

/** 释放request内存 **/
static void _minihttp_request_free (MiniHTTPDRequest *request) {
    size_t i = 0;

    if (request->get_keys) {
        for (i = 0; i < request->get_len; i++) {
            if (request->get_keys[i]) {
                free(request->get_keys[i]);
                free(request->get_datas[i]);
            }
        }
        free(request->get_keys);
        free(request->get_datas);
    }
    
    if (request->head_keys) {
        for (i = 0; i < request->head_len; i++) {
            free(request->head_keys[i]);
            free(request->head_datas[i]);
        }
        free(request->head_keys);
        free(request->head_datas);
    }

    if (request->post_keys) {
        for (i = 0; i < request->post_len; i++) {
            if (request->post_keys[i]) {
                free(request->post_keys[i]);
                free(request->post_datas[i]);
            }
        }
        free(request->post_keys);
        free(request->post_datas);
    }

    if (request->file_keys) {
        for (i = 0; i < request->file_len; i++) {
            if (request->file_keys[i]) {
                remove(request->file_path[i]);
                free(request->file_keys[i]);
                free(request->file_name[i]);
                free(request->file_path[i]);
            }
        }
        free(request->file_keys);
        free(request->file_name);
        free(request->file_path);
    }

    if (strlen(request->post_tmp) != 0) {
        remove(request->post_tmp);
    }
}

typedef struct MiniHTTPDResponse {
    int i;
} MiniHTTPDResponse;

static void _minihttpd_http200 (int sock, const char *s) {
    char buf[256] = {0};

    snprintf(buf, sizeof(buf) - 1, 
        "HTTP/1.1 200 OK\r\n"
        "Content-Length: %d\r\n"
        "\r\n", strlen(s));
    cSOCK_SendTXT(sock, buf);
    cSOCK_SendTXT(sock, s);
}

static void _minihttpd_http200_file (int sock, FILE *handle) {
    long size = 0;
    char buf[256] = {0}, temp[MHD_SIZE] = {0};
    int ret = 0, ret2 = 0;

    fseek(handle, 0, SEEK_END);
    size = ftell(handle);
    fseek(handle, 0, SEEK_SET);

    if (size == 0) {
        snprintf(buf, sizeof(buf) - 1, 
            "HTTP/1.1 200 OK\r\n"
            "Content-Length: 0\r\n"
            "\r\n");
        cSOCK_SendTXT(sock, buf);
        return;
    }

    snprintf(buf, sizeof(buf) - 1, 
        "HTTP/1.1 200 OK\r\n"
        "Content-Length: %d\r\n"
        "\r\n", size);
    cSOCK_SendTXT(sock, buf);

    ret = fread(temp, 1, MHD_SIZE, handle);
    while (ret) {
        ret2 = cSOCK_Send(sock, temp, ret);
        if (ret != ret2) break;
        ret = fread(temp, 1, MHD_SIZE, handle);
    }

}

static void _minihttpd_http404 (int sock) {
    cSOCK_SendTXT(sock, 
        "HTTP/1.1 404 Not Found\r\n"
        "\r\n");
}

static void _minihttpd_http500 (int sock) {
    cSOCK_SendTXT(sock, 
        "HTTP/1.1 500 Internal Server Error\r\n"
        "\r\n");
}

// +-------------------------------------------------
// | 具体实现 
// +-------------------------------------------------

static void minihttpd_get  (int sock, MiniHTTPDRequest *request);
static void minihttpd_post (int sock, MiniHTTPDRequest *request);

/** 检查错误 **/
#define MINI_MAIN_CHECK_RET(ret, sock, request) \
if (ret == 0) { _minihttp_request_free(&request); cSOCK_CloseNow(sock); return; }

/** 处理主函数 **/
static void minihttpd_main (int sock) {
    int  ret = 0;
    char tmp[MHD_SIZE] = {0};

    MiniHTTPDRequest request  = {0};
    memset(&request, 0, sizeof(MiniHTTPDRequest));

    ret = cSOCK_ReadLine(sock, tmp, sizeof(tmp) - 1);
    MINI_MAIN_CHECK_RET(ret, sock, request);
    ret = _minihttpd_decode_gp(tmp, &request);
    MINI_MAIN_CHECK_RET(ret, sock, request);

    while (1) {
        memset(tmp, 0, sizeof(tmp));
        ret = cSOCK_ReadLine(sock, tmp, sizeof(tmp) - 1);
        MINI_MAIN_CHECK_RET(ret, sock, request);
        if (strcmp(tmp, "\r\n") == 0) break;
        _minihttp_request_decode_head(tmp, &request);
    }

    /** 接受POST数据 **/
    if (request.method == MINI_HTTPD_POST) {
        if (!_minihttpd_decode_post(sock, &request)) {
            _minihttpd_http500(sock);
            cSOCK_Close(sock);
            _minihttp_request_free(&request);
            return;
        }
    }

    if (request.method == MINI_HTTPD_GET) {
        minihttpd_get(sock, &request);
    } else {
        minihttpd_post(sock, &request);
    }

    _minihttp_request_free(&request);
    cSOCK_Close(sock);
}

// +-------------------------------------------------
// | GET/POST分支 
// +-------------------------------------------------

/** 业务层函数声明 **/
static void time_service (int sock);
static void dump_service (MiniHTTPDRequest *request, int sock);

/** 输出文件 **/
static void minihttpd_sendfile (int sock, MiniHTTPDRequest *request) {
    char fullpath[MHD_SIZE] = {0};
    FILE *handle = NULL;

    const char *default_index[] = {"index.html", "index.htm"};
    size_t i = 0, default_len = 2;

    if (request->path[strlen(request->path) - 1] != '/') {
        snprintf(fullpath, sizeof(fullpath) - 1, "%s%s", MHD_WWW_ROOT, request->path);
        handle = fopen(fullpath, "rb");
    } else {
        for (i = 0; i < default_len; i++) {
            memset(fullpath, 0, sizeof(fullpath));
            snprintf(fullpath, sizeof(fullpath) - 1, "%s%s%s", MHD_WWW_ROOT, request->path, default_index[i]);
            if (handle = fopen(fullpath, "rb")) break;
        }
    }

    if (!handle) {
        _minihttpd_http404(sock);
        return;
    }

    _minihttpd_http200_file(sock, handle);
    fclose(handle);
}

static void minihttpd_get (int sock, MiniHTTPDRequest *request) {

    if (strcmp(request->path, "/time") == 0) {
        time_service(sock);
    }

    else {
        minihttpd_sendfile(sock, request);
    }
}

static void minihttpd_post (int sock, MiniHTTPDRequest *request) {

    if (strcmp(request->path, "/dump") == 0) {
        dump_service(request, sock);
    }

    else {
        _minihttpd_http404(sock);
    }
}

// +-------------------------------------------------
// | 业务层 
// +-------------------------------------------------

static void time_service (int sock) {
    time_t t = time(NULL);
    struct tm *fmTime = localtime(&t);
    char s[125] = {0};

    snprintf(s, sizeof(s) - 1, "%04d-%02d-%02d %02d:%02d:%02d", 
        fmTime->tm_year + 1900, fmTime->tm_mon + 1, fmTime->tm_mday,
        fmTime->tm_hour, fmTime->tm_min, fmTime->tm_sec);
    _minihttpd_http200(sock, s);
}

static void dump_service (MiniHTTPDRequest *request, int sock) {
    char tempHTML[1024] = {0};

    const char *get_datas = NULL;
    char **post_datas = NULL, **file_names = NULL, **file_paths;
    size_t post_len = 0, file_len = 0;

    get_datas = _minihttpd_request_get_get(request, "name");
    post_datas = _minihttpd_request_get_post(request, "user", &post_len);
    file_len = _minihttpd_request_get_file(request, "file", &file_names, &file_paths);

    if (get_datas) {
        strncat(tempHTML, "name=", sizeof(tempHTML) - 1);
        strncat(tempHTML, get_datas, sizeof(tempHTML) - 1);
        strncat(tempHTML, "\n", sizeof(tempHTML) - 1);
    }

    if (post_datas) {
        strncat(tempHTML, "user=", sizeof(tempHTML) - 1);
        strncat(tempHTML, post_datas[0], sizeof(tempHTML) - 1);
        strncat(tempHTML, "\n", sizeof(tempHTML) - 1);
    }

    if (file_len != 0) {
        strncat(tempHTML, "file=", sizeof(tempHTML) - 1);
        strncat(tempHTML, file_names[0], sizeof(tempHTML) - 1);
        strncat(tempHTML, ",", sizeof(tempHTML) - 1);
        strncat(tempHTML, file_paths[0], sizeof(tempHTML) - 1);
        strncat(tempHTML, "\n", sizeof(tempHTML) - 1);
    }

    FREE(post_datas);
    FREE(file_names);
    FREE(file_paths);

    _minihttpd_http200(sock, tempHTML);
}


